package dev.ece.suit.loader.models.fbx;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import dev.ece.core.cells.model.action.ActionManager.Action;
import dev.ece.suit.loader.Face;
import dev.ece.suit.loader.Joint;
import dev.ece.suit.loader.Material;
import dev.ece.suit.loader.Mesh;
import dev.ece.suit.loader.ModelDepthShader;
import dev.ece.suit.loader.ModelLoader;
import dev.ece.suit.loader.ModelShader;
import dev.ece.util.io.LittleEndianInputStream;
import dev.ece.util.lib.Vector3f;

public class FBXModelLoader implements ModelLoader<Float> {
	
	protected final static Logger LOG = LogManager.getLogger(FBXModelLoader.class);

	public final static String CONNECTIONS = "Connections";
	
	public final static String TAKES = "Takes";
	
	public final static String OBJECTS = "Objects";
	
	public final static String MESH = "Mesh";
	
	public final static String PROPERTIES70 = "Properties70";
	
	public final static String TYPE_FLAGS = "TypeFlags";
	
	public final static String P = "P";
	
	private LittleEndianInputStream input;
	
	private FBXHeader header;
	
	private Map<String, FBXElement> elements;
	
	private FBXMaterial[] materials;
	
	private FBXTexture[] textures;
	
	private FBXGeometry[] geometries;
	
	private FBXJoint[] joints;
	
	private FBXUpdateJoint[] updateJoints;
	
	private FBXSkin[] skins;
	
	private FBXAnimationStack[] stacks;
	
	private FBXAnimationLayer[] layers;
	
	private FBXAnimationCurve[] curves;
	
	private FBXAnimationCurveNode[] nodes;
	
	private FBXPose[] poses;
	
	private FBXNodeAttribute[] nodeAttributes;
	
	private FBXModel[] models;
	
	private Vector3f[] boundBox;
	
	public FBXModelLoader(InputStream input) throws IOException {
		this.input = new LittleEndianInputStream(input);
	}
	
	@Override
	public void load() throws IOException {
		header = FBXHeader.loadFbxHeader(input);
		LOG.debug("版本：{}", header.getVersion());
		elements = FBXElement.loadFBXElements(input, header);
		FBXElement connections = elements.get(CONNECTIONS);
		if(connections == null) {
			throw new NullPointerException("缺少Connections");
		}
		FBXElement takes = elements.get(TAKES);
		if(takes == null) {
			throw new NullPointerException("缺少Takes");
		}
		FBXElement objects = elements.get(OBJECTS);
		if(objects == null) {
			throw new NullPointerException("缺少Objects");
		}
		Map<String, List<FBXElement>> map = objects.toFBXElementMap();
		if(!map.containsKey(FBXGeometry.GEOMETRY)) {
			throw new NullPointerException("缺少Geometry");
		}
		if(!map.containsKey(FBXTexture.TEXTURE)) {
			throw new NullPointerException("缺少Texture");
		}
		if(!map.containsKey(FBXMaterial.MATERIAL)) {
			throw new NullPointerException("缺少Material");
		}
		geometries = FBXGeometry.parseFBXGeometry(map.get(FBXGeometry.GEOMETRY));
		boundBox = FBXGeometry.boundBox(geometries);
		LOG.debug("几何图形数量：{}", geometries.length);
		
		textures = FBXTexture.parseFBXTexture(map.get(FBXTexture.TEXTURE));
		LOG.debug("贴图数量：{}", textures.length);
		
		materials = FBXMaterial.parseFBXMaterial(map.get(FBXMaterial.MATERIAL));
		LOG.debug("材质数量：{}", materials.length);
		//关节骨骼信息
		List<FBXElement> deformers = map.get(FBXJoint.DEFORMER);
		joints = FBXJoint.parseFBXJoint(deformers);
		LOG.debug("关节数量：{}", joints != null ? joints.length : 0);
		//皮肤
		skins = FBXSkin.parseFBXSkin(deformers);
		LOG.debug("蒙皮数量：{}", skins != null ? skins.length : 0);
		//动画
		stacks = FBXAnimationStack.parseFBXAnimationStack(map.get(FBXAnimationStack.ANIMATION_STACK));
		LOG.debug("动画数量：{}", stacks != null ? stacks.length : 0);
		layers = FBXAnimationLayer.parseFBXAnimationLayer(map.get(FBXAnimationLayer.ANIMATION_LAYER));
		LOG.debug("动画层数：{}", layers != null ? layers.length : 0);
		curves = FBXAnimationCurve.parseFBXAnimationCurve(map.get(FBXAnimationCurve.ANIMATION_CURVE));
		LOG.debug("动画曲线数量：{}", curves != null ? curves.length : 0);
		nodes = FBXAnimationCurveNode.parseFBXAnimationCurveNode(map.get(FBXAnimationCurveNode.ANIMATION_CURVE_NODE));
		LOG.debug("动画曲线节点数量：{}", nodes != null ? nodes.length : 0);
		models = FBXModel.parseFBXModel(map.get(FBXModel.MODEL));
		LOG.debug("动画model数量：{}", models != null ? models.length : 0);
		poses = FBXPose.parseFBXPose(map.get(FBXPose.POSE));
		LOG.debug("动画pose数量：{}", poses != null ? poses.length : 0);
		nodeAttributes = FBXNodeAttribute.parseFBXNodeAttribute(map.get(FBXNodeAttribute.NODE_ATTRIBUTE));
		LOG.debug("node参数数量：{}", nodeAttributes != null ? nodeAttributes.length : 0);
		//更新骨骼使用
		updateJoints = models != null && joints != null ? new FBXUpdateJoint[] {new FBXUpdateJoint(models, joints)} : null;
		//关联
		Map<Long, List<FBXConnection>> conns = FBXConnection.toFBXConnectionsMap(FBXConnection.parseFBXConnections(connections));
		//关联纹理到材质
		FBXConnection.connectTextureToMaterial(conns, textures, materials);
		//关联材质到几何图形
		FBXConnection.connectMaterialToGeometry(conns, materials, geometries);
		//动画曲线关联动画节点
		FBXAnimationCurve.connectToFBXAnimationCurveNode(conns, curves, nodes);
		//曲线节点关联model
		FBXAnimationCurveNode.connectToFBXModel(conns, nodes, models);
		//node参数关联
		FBXNodeAttribute.connectToFBXModel(conns, nodeAttributes, models);
		//model关联关节
		FBXModel.connectToFBXJoint(conns, models, joints);
		//pose关联关节
		FBXPose.connectFBXJointModel(conns, poses, joints, models);
		//关联关节信息到几何图形
		FBXSkin.connectJointToGeometry(conns, skins, joints, geometries, models);
		
		FBXModel.setup(models);
		FBXJoint.setup(joints);
	}

	@Override
	public Float total() {
		return null;
	}

	@Override
	public int frameCount() {
		return 0;
	}

	@Override
	public Vector3f[] boundBox() {
		return this.boundBox;
	}

	@Override
	public Material[] getMaterials() {
		return this.materials;
	}

	@Override
	public Mesh[] getMeshes() {
		return FBXMesh.parseFBXMesh(joints, geometries);
	}

	@Override
	public Face[] getFaces() {
		return null;
	}

	@Override
	public Joint<Float>[] getJoints() {
		return joints;
	}

	@Override
	public Joint<Float>[] getUpdateJoints() {
		return updateJoints;
	}

	@Override
	public ModelShader getModelShader() {
		return ModelShader.getDefaultModelShader();
	}

	@Override
	public ModelDepthShader getModelDepthShader() {
		return ModelDepthShader.getDefaultModelDepthShader();
	}

	@Override
	public void addAction(Map<String, Action<Float>> actions) {
		actions.put("all", new Action<Float>("all", 0f, 0.866667f, true));
	}

	@Override
	public void dispose() {
		if(input != null) {
			input.close();
		}
		if(elements != null) {
			elements.clear();
			elements = null;
		}
		header = null;
		geometries = null;
		textures = null;
		materials = null;
		joints = null;
		skins = null;
		stacks = null;
		layers = null;
		curves = null;
		nodes = null;
		poses = null;
		models = null;
	}

}
